package com.grape.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.grape.domain.Account;
import com.grape.domain.Bill;
import com.grape.mapper.AccountMapper;
import com.grape.service.IAccountService;
import com.grape.constants.Constants;
import com.grape.dto.ElectronicAccountDTO;
import com.grape.dto.FinancialAccountDTO;
import com.grape.dto.StatisticAccountDTO;
import com.grape.utils.BigDecimalUtil;
import com.grape.utils.StringUtils;
import com.grape.utils.ThreadLocalUtil;
import com.grape.vo.BaseVo;
import com.grape.wrapper.BaseWrapper;
import lombok.AllArgsConstructor;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 *
 * 账户表 服务实现类
 *
 * @author      putao
 * @email       2628279194@qq.com
 */
@Service
@AllArgsConstructor
@Transactional(readOnly = true)
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements IAccountService {

    private final AccountMapper accountMapper;

    private static final Logger logger = org.slf4j.LoggerFactory.getLogger(AccountServiceImpl.class);

    /**
     * 分页
     * @param baseVo    统一分页对象
     * @return          账户表集合
     */
    @Override
    public List<Account> listData(BaseVo baseVo) {
        QueryWrapper<Account> wrapper = new BaseWrapper<Account>().init();
        wrapper.like(Strings.isNotBlank(baseVo.getSearch()), Constants.NAME, baseVo.getSearch())
                .eq(StringUtils.isNotBlank(baseVo.getColumnName()) && StringUtils.isNotBlank(baseVo.getSelect()), baseVo.getColumnName(), baseVo.getSelect());
        orderByModifyTimeDesc(wrapper);
        return this.list(wrapper);
    }

    /**
     * 自定义分页
     * @param   baseVo      统一分页对象
     */
    @Override
    public void pageListCustom(BaseVo baseVo) {
        String userId = ThreadLocalUtil.getUserId();
        QueryWrapper<Account> qw = new QueryWrapper<Account>().eq(Constants.USER_ID, userId)
                .orderByDesc(Constants.CREATE_TIME);
        this.accountMapper.selectList(qw);
    }

    /**
     * 修改
     * @param account 账户表对象
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void updateData(Account account) {
        this.checkIsRepeat(null, account, Constants.NAME, MessageFormat.format("账户名字：{0} 已存在", account.getName()));
        this.baseUpdate(account);
    }

    /**
     * 新增
     * @param       account 账户表对象
     * @return      账户表对象
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public Account insertData(Account account) {
        account.setUserId(ThreadLocalUtil.getUserId());
        this.checkIsRepeat(null, account, Constants.NAME, MessageFormat.format("账户名字：{0} 已存在", account.getName()));
        if (account.getInitMoney() == null) {
            account.setInitMoney(BigDecimal.ZERO);
        }
        account.setProperty(account.getInitMoney());
        account.setTotalExpenses(BigDecimal.ZERO);
        return this.baseInsert(account);
    }

    /**
     * 删除
     * @param id    单据id
     * @return      删除条数
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public int deleteData(String id) {
        return this.baseDelete(id);
    }

    /**
     * 根据id查询详情
     * @param       id  单据id
     * @return      账户表对象
     */
    @Override
    public Account detail(String id) {
        return this.getByIdForDelFlag(id);
    }

    /**
     * 查询当前用户全部账户
     * @param accountBookId   账本id
     * @return 账户列表
     */
    @Override
    public List<Account> listAll(String accountBookId) {
        return accountMapper.selectList(new BaseWrapper<Account>().initDelFlagAndUserId().eq(StringUtils.isNotBlank(accountBookId), Constants.ACCOUNT_BOOK_ID, accountBookId));
    }

    /**
     * 根据账单列表和账户id更新账户金额和总支出
     * @param billList  账单列表
     * @param accountIdList 账户id
     */
    @Override
    @Transactional(readOnly = false)
    public void updateMoney(List<Bill> billList, List<String> accountIdList) {
        if (CollectionUtil.isEmpty(billList)) {
            logger.info("[updateMoney] 账单列表为空，不进行更新金额");
            return;
        }
        if (CollectionUtil.isEmpty(accountIdList)) {
            logger.info("[updateMoney] 账户id为空，不进行更新金额");
            return;
        }
        List<Account> accountList = this.listAppendDelAndUserId(accountQueryWrapper -> accountQueryWrapper.in(Constants.ID, accountIdList));
        if (CollectionUtil.isEmpty(accountList)) {
            return;
        }
        Map<String, Map<String, List<Bill>>> billMap = billList.stream().collect(Collectors.groupingBy(Bill::getAccountId, Collectors.groupingBy(Bill::getCategoryType)));
        List<Account> updateAccountList = new ArrayList<>();
        for (Account account : accountList) {
            if (billMap.containsKey(account.getId())) {
                Map<String, List<Bill>> typeMap = billMap.get(account.getId());
                List<Bill> payBillList = typeMap.get(Constants.CATEGORY_TYPE_PAY);
                if (CollectionUtil.isNotEmpty(payBillList)) {
                    account.setTotalExpenses(payBillList.stream().map(Bill::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add));
                } else {
                    account.setTotalExpenses(BigDecimal.ZERO);
                }
                List<Bill> incomeBillList = typeMap.get(Constants.CATEGORY_TYPE_INCOME);
                if (CollectionUtil.isNotEmpty(incomeBillList)) {
                    account.setProperty(BigDecimalUtil.add(incomeBillList.stream().map(Bill::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add), account.getInitMoney()));
                } else {
                    account.setProperty(account.getInitMoney());
                }
            } else {
                account.setTotalExpenses(BigDecimal.ZERO);
                account.setProperty(account.getInitMoney());
            }
            account.updateInit();
            updateAccountList.add(account);
        }
        if (CollectionUtil.isNotEmpty(updateAccountList)) {
            this.updateBatchById(updateAccountList);
        }
    }

    /**
     * 统计账户信息
     * @param accountBookId  账本id
     * @return 统计信息
     */
    @Override
    public StatisticAccountDTO statistic(String accountBookId) {
        List<Account> accountList = this.list(new BaseWrapper<Account>().initDelFlagAndUserId().eq(StringUtils.isNotBlank(accountBookId), Constants.ACCOUNT_BOOK_ID, accountBookId));
        if (CollectionUtil.isEmpty(accountList)) {
            return null;
        }
        StatisticAccountDTO statisticAccountDTO = new StatisticAccountDTO();
        int electronicAccountQuantity = 0;
        int financialAccountQuantity = 0;
        BigDecimal totalProperty = BigDecimal.ZERO;
        List<FinancialAccountDTO> financialAccountDTOList = new ArrayList<>();
        statisticAccountDTO.setFinancialAccountDTOList(financialAccountDTOList);
        List<ElectronicAccountDTO> electronicAccountDTOList = new ArrayList<>();
        statisticAccountDTO.setElectronicAccountDTOList(electronicAccountDTOList);
        for (Account account : accountList) {
            if (Constants.ACCOUNT_TYPE_ELECTRONIC.equals(account.getType())) {
                electronicAccountQuantity++;
                ElectronicAccountDTO electronicAccountDTO = new ElectronicAccountDTO();
                electronicAccountDTO.initByAccount(account);
                electronicAccountDTOList.add(electronicAccountDTO);
            } else {
                financialAccountQuantity++;
                FinancialAccountDTO financialAccountDTO = new FinancialAccountDTO();
                financialAccountDTO.initByAccount(account);
                financialAccountDTOList.add(financialAccountDTO);
            }
            totalProperty = BigDecimalUtil.add(totalProperty, account.getProperty());
        }
        statisticAccountDTO.setElectronicAccountQuantity(electronicAccountQuantity);
        statisticAccountDTO.setFinancialAccountQuantity(financialAccountQuantity);
        statisticAccountDTO.setTotalProperty(BigDecimalUtil.getDoubleValue(totalProperty));
        return statisticAccountDTO;
    }
}
